#include "ResourceFactory.h"
#include "ResSprite.h"
#include "ResText.h"
#include "ResBitmapFont.h"
#include "ResBitmapText.h"
#include "Helpers.h"
#include "D3D9Engine.h"


CResourceFactory::CResourceFactory(CD3D9Engine* ptrEngine): _ptrEngine(ptrEngine)
{
	this->m_textureRegistry = new std::vector<_TEXREG*>;
	this->m_soundBufferRegistry = new std::vector<_SNDBUFFREG*>;
}


CResourceFactory::~CResourceFactory()
{
	LogEntry(Helper::StrConcat("Texture manager has %i foreign element(s) at delete.", (int)this->m_textureRegistry->size()));
	LogEntry(Helper::StrConcat("Soundbuffer manager has %i foreign element(s) at delete.", (int)this->m_soundBufferRegistry->size()));

	std::vector<_TEXREG*>::iterator iter;
	for(iter=this->m_textureRegistry->begin(); 
		iter != this->m_textureRegistry->end(); ++iter)
	{
		_TEXREG* t = (*iter);
		SAFE_RELEASE((*iter)->texture);
		SAFE_DELETE(t);
	}

	std::vector<_SNDBUFFREG*>::iterator snditer;

	for (snditer = this->m_soundBufferRegistry->begin();
		 snditer != this->m_soundBufferRegistry->end();
		 ++snditer)
	{
		_SNDBUFFREG *temp = (*snditer);
		_ptrEngine->GetSoundAPI()->ReleaseBuffers(1, &temp->bufferID);

		SAFE_DELETE(temp);
	}

	SAFE_DELETE(this->m_textureRegistry);
	SAFE_DELETE(this->m_soundBufferRegistry);
}


IResourceObject* CResourceFactory::CreateResource(RESOURCETYPE type)
{
	IResourceObject* resObject = NULL;

	switch(type)
	{
	case CASGE_RES_SPRITE:
		resObject = new CResourceSprite();

		break;
	case CASGE_RES_TEXT:
		resObject = new CResourceText();

		break;
	case CASGE_RES_BMPFONT:
		resObject = new CResourceBitmapFont();

		break;
	case CASGE_RES_BMPTEXT:
		resObject = new CResourceBitmapText();

		break;

	case CASGE_RES_SNDSOURCE:
		resObject = new CResourceSoundSource();
		break;
	}

	return resObject;
}


LPDIRECT3DTEXTURE9 CResourceFactory::RegisterTexture(const char *fileName, int* width, int* height, DWORD colorKey)
{
	if(fileName == NULL)
		return NULL;

	unsigned int len = (unsigned int)strlen(fileName);
	unsigned int hash = Helper::SuperFastHash(fileName, len);
	bool emptySlot = false;
	_TEXREG* emptyTexReg = NULL;

	std::vector<_TEXREG*>::iterator iter;
	for(iter = this->m_textureRegistry->begin();
		iter != this->m_textureRegistry->end();
		++iter)
	{
		if((*iter)->hash == hash &&
			(*iter)->ref != 0)
		{
			(*iter)->ref++;
#ifdef _DEBUG
			LogEntry(Helper::StrConcat("Texture registering, already registered: %s, hash: %i, ref.: %i", fileName, (*iter)->hash, (*iter)->ref));
#endif
			return (*iter)->texture;
		}
		else if((*iter)->hash == 0 &&
			(*iter)->ref == 0)
		{
			if(emptySlot == false)
			{
				emptyTexReg = (*iter);
				emptySlot = true;
			}
		}
	}

	LPDIRECT3DTEXTURE9	lpTexture = NULL;
	HRESULT res;
	D3DXIMAGE_INFO	imgInfo;

	res = D3DXCreateTextureFromFileEx(this->_ptrEngine->GetDevice(),
										   fileName,
											(*width > 0)? *width : D3DPOOL_DEFAULT, 
											(*height > 0)? *height : D3DPOOL_DEFAULT,
										   1,
										   D3DPOOL_DEFAULT,
										   D3DFMT_UNKNOWN,
										   D3DPOOL_DEFAULT,
										   D3DX_DEFAULT,
										   D3DX_DEFAULT,
										   colorKey,
										   &imgInfo,
										   NULL,
										   &lpTexture);

	if(SUCCEEDED(res) &&
		lpTexture != NULL)
	{

		this->_ptrEngine->GetDevice()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		this->_ptrEngine->GetDevice()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		this->_ptrEngine->GetDevice()->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);


		if(*width == 0)	(*width) = imgInfo.Width;
		if(*height == 0) (*height) = imgInfo.Height;

		_TEXREG* ptrTex = NULL;

		if(emptySlot)
		{
			ptrTex = emptyTexReg;
		}
		else
		{
			ptrTex = new _TEXREG;
		}

		ptrTex->texture = lpTexture;
		ptrTex->ref = 1;
		ptrTex->hash = hash;

		this->m_textureRegistry->push_back(ptrTex);
	}

#ifdef _DEBUG
	if(lpTexture == NULL)
		LogEntry(Helper::StrConcat("Registering texture failed: %s ! NULL texture pointer!", fileName));
	else
		LogEntry(Helper::StrConcat("Texture registered: %s, hash: %i, ref.: %i", fileName, hash, 1));
#endif

	return lpTexture;
}


void CResourceFactory::UnregisterTexture(const char *fileName)
{
	if(fileName == NULL)
		return;

	unsigned int len = (unsigned int)strlen(fileName);
	unsigned int hash = Helper::SuperFastHash(fileName, len);

	std::vector<_TEXREG*>::iterator iter;
	for(iter = this->m_textureRegistry->begin();
		iter != this->m_textureRegistry->end();
		++iter)
	{
		if((*iter)->hash == hash)
		{
			(*iter)->ref--;

			if((*iter)->ref == 0)
			{
				SAFE_RELEASE((*iter)->texture);
#ifdef _DEBUG
				LogEntry(Helper::StrConcat("Texture released (no more ref.): %s, hash: %i!", fileName, (*iter)->hash));
#endif
				(*iter)->hash = 0;

				_TEXREG* ptrItem = *iter;
				this->m_textureRegistry->erase(iter);

				SAFE_DELETE(ptrItem);

				return;
			}
		}
	}
}

ALuint CResourceFactory::RegisterSoundBuffer(const char *fileName, SND_FILETYPE fileType)
{
	if (fileName == NULL ||
		fileType == SNDFT_NULL)
		return 0;

	unsigned int length = (unsigned int) strlen(fileName);
	unsigned int hash = Helper::SuperFastHash(fileName, length);
	bool emptySlot = false;
	_SNDBUFFREG* emptyBuffReg = NULL;

	std::vector<_SNDBUFFREG*>::iterator iter;

	for (iter = this->m_soundBufferRegistry->begin();
		 iter != this->m_soundBufferRegistry->end();
		 ++iter)
	{
		if ((*iter)->hash == hash &&
			(*iter)->ref != 0)
		{
			(*iter)->ref++;
#ifdef _DEBUG
			LogEntry(Helper::StrConcat("Soundbuffer registering, already registered: %s, hash: %i, ref.: %i", fileName, (*iter)->hash, (*iter)->ref));
#endif
			return (*iter)->bufferID;
		}
		else if ((*iter)->hash == 0 &&
				 (*iter)->ref == 0)
		{
			if (emptySlot == false)
			{
				emptyBuffReg = (*iter);
				emptySlot = true;
			}
		}
	}

	ALuint bufferID;
	_SNDBUFFREG *buffReg = NULL;

	if (this->_ptrEngine->GetSoundAPI()->GenerateBuffers(1, &bufferID) == false)
		return 0;

	switch (fileType)
	{
	case SNDFT_WAV:
		{
			Helper::WAVDATA data;

			Helper::LoadWAVFile(fileName, &data);

			ALenum format = 0x1100;

			if (data.Channels == 2)			format += 2;
			if (data.BitsPerSample == 16)	format += 1;

			alBufferData(bufferID, format, data.Data, data.DataSize, data.SampleRate);

			break;
		}
	}

	if (alGetError() == AL_NO_ERROR)
	{
		if(emptySlot)
		{
			buffReg = emptyBuffReg;
		}
		else
		{
			buffReg = new _SNDBUFFREG;
		}

		buffReg->hash = hash;
		buffReg->ref = 1;
		buffReg->bufferID = bufferID;

		this->m_soundBufferRegistry->push_back(buffReg);
	}

	return buffReg->bufferID;
}

void CResourceFactory::UnregisterSoundBuffer(const char *fileName)
{
	if (fileName == NULL)
		return;

	unsigned int length = (unsigned int) strlen(fileName);
	unsigned int hash = Helper::SuperFastHash(fileName, length);

	std::vector<_SNDBUFFREG*>::iterator iter;

	for(iter = this->m_soundBufferRegistry->begin();
		iter != this->m_soundBufferRegistry->end();
		++iter)
	{
		if ((*iter)->hash == hash)
		{
			(*iter)->ref--;

			if ((*iter)->ref == 0)
			{
				this->_ptrEngine->GetSoundAPI()->ReleaseBuffers(1, &(*iter)->bufferID);

#ifdef _DEBUG
				LogEntry(Helper::StrConcat("Soundbuffer released (no more ref.): %s, hash: %i!", fileName, (*iter)->hash));
#endif

				(*iter)->hash = 0;

				_SNDBUFFREG* ptrSndBuffer = (*iter);
				this->m_soundBufferRegistry->erase(iter);

				SAFE_DELETE(ptrSndBuffer);

				break;
			}
		}
	}
}